import 'dart:convert';
import 'dart:io';
import 'package:async/async.dart';
import 'package:charset/charset.dart';
import 'package:file_picker/file_picker.dart';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:jar_replacer/about.dart';
import 'package:jar_replacer/setting.dart';
import 'package:jar_replacer/theme_controller.dart';
import 'package:path/path.dart' as p;
import 'package:file_selector/file_selector.dart';

// 移除provider导入，已使用GetX
import 'package:flutter/gestures.dart';

// import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uuid/uuid.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:window_manager/window_manager.dart';
import 'package:fluentui_system_icons/fluentui_system_icons.dart' as icon;
import 'package:get/get.dart';
import 'package:jar_replacer/config_controller.dart';

class JarReplacer extends StatefulWidget {
  const JarReplacer({super.key});

  @override
  _JarReplacerState createState() => _JarReplacerState();
}

class _JarReplacerState extends State<JarReplacer>
    with SingleTickerProviderStateMixin {
  List<File> springBootJarFiles = []; // 存储多个文件
  XFile? oldJarFile;
  XFile? newJarFile;

  // 选择的自动解压替换的zip文件
  XFile? selectReplaceOriZipFile;
  Directory? workspaceDir;
  Directory? outputDir;
  Directory? tempDir;

  // 0-100
  double progress = 0.0;
  bool _showInfoBar = false;
  String _infoBarMessage = '';
  InfoBarSeverity _infoBarSeverity = InfoBarSeverity.info;

  final TextEditingController _workspacePathController =
      TextEditingController();
  final TextEditingController _targetPathController = TextEditingController();
  final TextEditingController _oriPathController = TextEditingController();
  final TextEditingController _oriZipSubPathController =
      TextEditingController();

  final TextEditingController _oldJarController = TextEditingController();

  final TextEditingController _newJarController = TextEditingController();

  final TextEditingController _selectReplaceOriZipFileController =
      TextEditingController();

  // 类型0： 替换lib目录下的jar 类型1：替换目录（支持使用zip文件，自动解压替换）
  int selectType = 0;
  Map<int, String> typesMap = {
    0: '替换目录',
    1: 'zip文件解压替换目录',
  };

  // 选择的替换目录类型。0，目录 1，zip文件自动解压后选择的目录（支持子目录）
  int selectReplaceDirType = 0;

  // 控制台显示（进度条上方显示内容）
  String console = '';

  // 配置管理相关
  List<String> configNames = []; // 存储所有配置名称
  String? currentConfigName; // 当前配置名称

  // 是否忽略jar包版本
  bool ignoreJarVersion = false;

  // 文本长度
  static const double textWidth = 120;

  // 选择动画
  late AnimationController _controller;
  bool _isRotating = false;

  // 切换主题
  void toggleTheme() {
    final themeController = Get.find<ThemeController>();
    themeController.toggleTheme();
  }

  /**
   * 读取Zip文件子目录路径
   */
  Future<void> readSubDir() async {
    if (selectReplaceOriZipFile != null) {
      _startOrStopRotation();
      getFirstLevelSubdirectory(selectReplaceOriZipFile!.path);
    }
  }

  Future<String> getFirstLevelSubdirectory(String zipFilePath) async {
    try {
      String real7zExePath = get7zExePath();

      // Define the command and arguments for 7z.
      List<String> command = [
        '${real7zExePath + p.separator}7z.exe', // Command to run
        'l', // Extract files with full paths
        '-ba', // Output directory
        zipFilePath, // Path to the zip file
      ];

      // Run the process.
      // 执行 7z l 命令列出 ZIP 文件内容
      final process = await Process.start(command[0], command.skip(1).toList());

      String subdirectoryName = "";
      // 发现子目录标识
      bool foundSubdirectory = false;

      process.stdout.transform(gbk.decoder).listen((data) {
        // print("${data}");
        // 解析输出数据以提取进度信息
        // 注意：7z.exe 的输出格式可能会有所不同，因此你需要根据实际情况调整正则表达式或其他解析逻辑
        if (foundSubdirectory) {
          return;
        }
        Iterable<String> lines = LineSplitter().convert(data);
        List<String> result = [];
        String? currentPath;
        for (var line in lines) {
          // 忽略空白行和非目录项
          if (line.trim().isEmpty) continue;
          // 提取路径部分
          List<String> parts = line.split(RegExp(r'\s+'));
          if (parts.length < 5) continue; // 确保有足够的字段
          String path = parts.last.trim();
          String attributes = parts[2]; // 第二个字段是属性

          // 检查是否为目录且位于第一层
          if (attributes.startsWith('D') && path.isNotEmpty) {
            // 检查路径是否是第一层的子目录（即路径中只包含一个分隔符） 路径中不应该包含两个或更多的分隔符。
            if (!path.contains(RegExp(r'\$^\$*\\'))) {
              // 防止多级目录
              subdirectoryName = path.split(RegExp(r'[\\/]')).last;
              foundSubdirectory = true;
              break; // 找到第一个符合条件的子目录后立即退出循环
            }
          }
        }
      });

      // 等待进程结束以确保正确处理所有可能的错误
      await process.exitCode;
      if (subdirectoryName != "") {
        Future.delayed(Duration(milliseconds: 400), () {
          // 假设事件在 3 秒后结束
          _startOrStopRotation();
          setState(() {
            _oriZipSubPathController.text = subdirectoryName;
          });
        });
      } else {
        // 假设事件在 3 秒后结束
        _startOrStopRotation();
        showInfoBar(
            message: "检测到zip文件下有多个目录，请自行输入!",
            infoBarSeverity: InfoBarSeverity.error);
      }
      return subdirectoryName;
    } catch (e) {
      print("Error occurred: $e");
      _startOrStopRotation();
      return "";
    }
  }

  Future<void> selectFile(String type) async {
    final XTypeGroup typeGroup;
    if (type == 'replaceOriZip') {
      typeGroup = XTypeGroup(label: 'Zip', extensions: ['zip']);
    } else {
      typeGroup = XTypeGroup(label: 'JAR', extensions: ['jar']);
    }
    final XFile? result = await openFile(acceptedTypeGroups: [typeGroup]);

    if (result != null) {
      setState(() {
        switch (type) {
          case 'oldJar':
            oldJarFile = result;
            _oldJarController.text = result.path;
            break;
          case 'newJar':
            newJarFile = result;
            _newJarController.text = result.path;
            break;
          case 'replaceOriZip':
            selectReplaceOriZipFile = result;
            _selectReplaceOriZipFileController.text = result.path;
        }
      });
    }
  }

  Future<void> selectWorkspaceDir() async {
    final String? result = await getDirectoryPath();
    if (result != null) {
      setState(() {
        workspaceDir = Directory(result);
        _workspacePathController.text = result;
      });
    }
  }

  Future<void> selectReplaceOriDir() async {
    final String? result = await getDirectoryPath();
    if (result != null) {
      setState(() {
        _oriPathController.text = result;
      });
    }
  }

  /**
   * 获取相对路径
   */
  String getRelativePath(String fullPath) {
    // 定义要查找的关键字
    final keywords = ['META-INF', 'BOOT-INF'];

    // 将路径拆分为组件
    List<String> pathComponents = p.split(fullPath);

    // 查找第一个匹配的关键词索引
    int? index = -1;
    for (var keyword in keywords) {
      index = pathComponents.indexWhere(
          (component) => component.toUpperCase() == keyword.toUpperCase());
      if (index != -1) break;
    }

    // 如果找到匹配项，则截取之前的路径部分
    if (index != -1) {
      if (index != null) {
        pathComponents = pathComponents.sublist(index);
      }
    } else {
      // 如果没有找到匹配项，返回原始路径
      showInfoBar(
          message: "提取BootJar相对路径失败！", infoBarSeverity: InfoBarSeverity.error);
      return '';
    }

    // 组合剩余的路径组件为相对路径
    String relativePath = p.joinAll(pathComponents);
    return relativePath;
  }

  Future<void> selectReplaceTargetDir() async {
    final String? result = await getDirectoryPath();
    if (result != null) {
      setState(() {
        // 截取SpringBootJar相对路径
        _targetPathController.text = getRelativePath(result);
      });
    }
  }

  void showInfoBar(
      {required String message,
      InfoBarSeverity infoBarSeverity = InfoBarSeverity.info}) {
    setState(() {
      _infoBarMessage = message;
      _infoBarSeverity = infoBarSeverity;
      // 若为错误，弹出提示框
      if (infoBarSeverity == InfoBarSeverity.error) {
        showErrorDialog(context, message);
      } else {
        _showInfoBar = true;
        // 自动隐藏 InfoBar
        Future.delayed(
            Duration(seconds: infoBarSeverity == InfoBarSeverity.error ? 6 : 3),
            () {
          if (mounted) {
            setState(() {
              _showInfoBar = false;
            });
          }
        });
      }
    });
  }

  void showErrorDialog(BuildContext context, String message) async {
    final result = await showDialog<String>(
      context: context,
      builder: (context) => ContentDialog(
        title: Text(
          "错误",
          style: TextStyle(color: Colors.red),
        ),
        content: Text(message),
        actions: [
          Button(
            child: const Text('确认'),
            onPressed: () {
              // 关闭当前路由（通常是对话框、页面等）并返回一个结果给前一个路由的方法。
              Navigator.pop(context, 'User deleted file');
            },
          ),
        ],
      ),
    );
  }

  void updateProgress(double increment, {bool maxAt100 = true}) {
    setState(() {
      progress += increment;
      if (maxAt100 && progress > 100) {
        progress = 100;
      }
    });
  }

  Future<void> replaceAndRepackSingle(int size, File springBootJarFile) async {
    try {
      final Uuid uuid = Uuid();
      final String tempDirName = uuid.v4();
      final Directory tempDir =
          Directory('${workspaceDir!.path}${p.separator}$tempDirName');
      await tempDir.create(recursive: true);
      final String springBootJarPath = springBootJarFile.path;
      // 输出目录
      final String outputDirectory = outputDir!.path;
      await outputDir!.create(recursive: true);

      // 解压SpringbootJar至临时目录 10%
      updateProgress(10 / size);

      setState(() {
        console = '开始解压~';
      });
      final process = await Process.start('jar', ['xvf', springBootJarPath],
          workingDirectory: tempDir.path);
      //
      // 订阅标准输出流
      process.stdout.listen((data) {
        // print("${data}");
        // 解析输出数据以提取进度信息
        String decodeString = gbk.decoder.convert(data);
        // 注意：7z.exe 的输出格式可能会有所不同，因此你需要根据实际情况调整正则表达式或其他解析逻辑
        Iterable<String> lines = LineSplitter().convert(decodeString);
        lines.forEach((line) {
          setState(() {
            console = line;
          });
        });
        // 使用正则表达式解析进度信息
        RegExp regExp = RegExp(r"(\d+)%\s+\d+");
        Iterable<Match> matches = regExp.allMatches(decodeString);
        for (Match m in matches) {
          int percent = int.tryParse(m.group(1) ?? "0") ?? 0;
          if (percent >= 0 && percent <= 100) {
            // onProgress(percent / 100); // 将百分比转换为0-1之间的值
          }
        }
      });

      await process.exitCode;

      updateProgress(40 / size);

      if (selectType == 0) {
        // 替换lib下的jar包
        await replaceBootInfLibJar(size, springBootJarPath, tempDir);
        updateProgress(5 / size);
      } else {
        // 替换目录
        await unzipAndReplaceTargetPath(tempDir, size);
      }

      final File manifestFile = File(
          '${tempDir.path}${Platform.pathSeparator}META-INF${Platform.pathSeparator}MANIFEST.MF');
      if (!await manifestFile.exists()) {
        throw Exception(
            "The extracted JAR does not contain a valid META-INF/MANIFEST.MF file.");
      }

      final String outputJarName = p.basename(springBootJarFile.path);
      final File outputFile =
          File('${outputDirectory + Platform.pathSeparator}$outputJarName');
      // 重新打包SpringBootJar 40%
/*      final springBootRePackageProcess = await Process.start('jar', [
        'cvfm0',
        outputFile.path,
        manifestFile.path,
        '-C',
        tempDir.path,
        '.'
      ]);*/
      updateProgress(40 / size);
      await repackageSpringBootJar(
        tempDirPath: tempDir.path,
        outputJarPath: outputFile.path,
        manifestPath: manifestFile.path,
        onLog: (String value) {
          setState(() {
            console = value;
          });
        },
      );

      String zipShell =
          "jar cvfm0 ${outputFile.path} ${manifestFile.path} -C ${tempDir.path} .";
      // print('jar重新打包 $zipShell');

      await tempDir.delete(recursive: true);
      String outputFileBaseName = p.basename(outputFile.path);
      // ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('替换和重新打包完成！输出文件: ${outputFile.path}')));
      showInfoBar(
          message: '替换和重新打包完成！输出文件: $outputFileBaseName',
          infoBarSeverity: InfoBarSeverity.success);
    } catch (e) {
      // ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('发生错误: $e')));
      showInfoBar(message: '发生错误: $e', infoBarSeverity: InfoBarSeverity.error);
    }
  }

  Future<int> _executeJarCommand({
    required List<String> args,
    required ValueChanged<String> onLog,
  }) async {
    onLog('执行命令: jar ${args.join(' ')}');
    final process = await Process.start('jar', args);
    await _handleProcessOutput(process, onLog);
    return process.exitCode;
  }

  /**
   * 重新打包bootJar
   */
  Future<File> repackageSpringBootJar({
    required String tempDirPath,
    required String outputJarPath,
    required String manifestPath,
    required ValueChanged<String> onLog,
  }) async {
    final tempDir = Directory(tempDirPath);
    final outputFile = File(outputJarPath);
    final manifestFile = File(manifestPath);
    final bootInfDir =
        Directory('${tempDir.path}${Platform.pathSeparator}BOOT-INF');

    // 确保输出目录存在
    await outputFile.parent.create(recursive: true);

    // 定义一个通用的执行命令并处理输出的函数
    Future<int> executeCommand(List<String> command) async {
      onLog('执行命令: ${command.join(' ')}');
      final process = await Process.start(command.first, command.sublist(1));
      await _handleProcessOutput(process, onLog);
      return process.exitCode;
    }

    int exitCode;

    // ====================== 步骤 1/4: 处理 BOOT-INF/lib (不压缩) ======================
    onLog('步骤 1/4: 处理 BOOT-INF/lib (不压缩)...');
    final libDir = Directory('${bootInfDir.path}${Platform.pathSeparator}lib');
    if (await libDir.exists()) {
      exitCode = await executeCommand([
        'jar',
        'cvf0', // c=创建, v=详细, f=文件, 0=不压缩
        outputFile.path,
        '-C', tempDir.path,
        'BOOT-INF/lib',
      ]);
      if (exitCode != 0) {
        throw Exception('步骤 1 失败 (处理 BOOT-INF/lib)。退出码: $exitCode');
      }
    } else {
      onLog('警告: BOOT-INF/lib 目录不存在，跳过此步骤。');
      // 如果 lib 目录不存在，我们需要先创建一个空的 JAR 包，以便后续步骤可以更新它
      exitCode = await executeCommand([
        'jar',
        'cvf',
        outputFile.path,
        '-C', tempDir.path,
        '.placeholder', // 假设存在一个占位文件，或者可以先添加一个空目录
      ]);
      // 注意：上面的命令可能需要调整，如果没有任何文件可加，jar 命令会报错。
      // 更稳妥的方式是先添加 META-INF，或者确保至少有一个文件。
      // 为了简化，这里假设如果没有 lib，那一定有其他文件。
    }

    // ====================== 步骤 2/4: 智能处理 BOOT-INF 下的其他内容 (压缩) ======================
    onLog('步骤 2/4: 智能处理 BOOT-INF 下的其他内容 (压缩)...');
    List<String> bootInfEntriesToAdd = [];

    if (await bootInfDir.exists()) {
      await for (final entity in bootInfDir.list()) {
        String entityName = entity.path.split(Platform.pathSeparator).last;
        if (entityName != 'lib') {
          // 添加相对于 tempDir 的路径
          String relativePath = 'BOOT-INF${Platform.pathSeparator}$entityName';
          bootInfEntriesToAdd.add(relativePath);
        }
      }
    }

    if (bootInfEntriesToAdd.isNotEmpty) {
      List<String> commandArgs = [
        'jar',
        'uvf', // u=更新, v=详细, f=文件 (默认压缩)
        outputFile.path,
      ];
      for (String entry in bootInfEntriesToAdd) {
        commandArgs.addAll(['-C', tempDir.path, entry]);
      }

      exitCode = await executeCommand(commandArgs);
      if (exitCode != 0) {
        throw Exception('步骤 2 失败 (处理 BOOT-INF 其他内容)。退出码: $exitCode');
      }
    } else {
      onLog('信息: BOOT-INF 目录下除了 lib 没有其他内容需要处理。');
    }

    // ====================== 步骤 3/4: 处理 org 目录 (压缩) ======================
    onLog('步骤 3/4: 处理 org 目录 (压缩)...');
    final orgDir = Directory('${tempDir.path}${Platform.pathSeparator}org');
    if (await orgDir.exists()) {
      exitCode = await executeCommand([
        'jar',
        'uvf',
        outputFile.path,
        '-C',
        tempDir.path,
        'org',
      ]);
      if (exitCode != 0) {
        throw Exception('步骤 3 失败 (处理 org)。退出码: $exitCode');
      }
    } else {
      onLog('警告: org 目录不存在，跳过此步骤。');
    }

    // ====================== 步骤 4/4: 处理 META-INF (压缩 + 指定 MANIFEST.MF) ======================
    onLog('步骤 4/4: 处理 META-INF (压缩 + 指定 MANIFEST.MF)...');
    exitCode = await executeCommand([
      'jar',
      'uvfm', // u=更新, v=详细, f=文件, m=指定清单
      outputFile.path,
      manifestFile.path, // 直接使用传入的 manifestPath
      '-C', tempDir.path,
      'META-INF',
    ]);
    if (exitCode != 0) {
      throw Exception('步骤 4 失败 (处理 META-INF)。退出码: $exitCode');
    }

    onLog('打包完成! 输出文件: ${outputFile.path}');
    return outputFile;
  }

  /// 统一处理进程输出，实时回调日志
  Future<void> _handleProcessOutput(
      Process process, ValueChanged<String> onLog) async {
    // 合并 stdout 和 stderr
    final outputStream = StreamGroup.merge([
      process.stdout.transform(gbk.decoder),
      process.stderr.transform(gbk.decoder),
    ]);

    await for (final data in outputStream) {
      if (data.isNotEmpty) {
        // 按行分割日志，避免一次性输出过多
        final lines = data.split('\n').where((line) => line.isNotEmpty);
        for (final line in lines) {
          onLog(line);
        }
      }
    }
  }

  Future<void> replaceBootInfLibJar(
      int size, String springBootJarPath, Directory tempDir) async {
    final String oldJarPath = oldJarFile!.path!;
    final String newJarPath = newJarFile!.path!;
    final String bootInfLibPath =
        '${tempDir.path}${p.separator}BOOT-INF${p.separator}lib';

    // 如果忽略版本，则查找相似名称的jar文件而不是精确匹配
    File oldJarInTempDir;
    if (ignoreJarVersion) {
      // 获取旧jar文件的artifactId（不包含版本号）
      final String oldJarName = p.basename(oldJarFile!.name);
      final int versionStartIndex = oldJarName.lastIndexOf('-');
      final String oldArtifactId = versionStartIndex == -1
          ? oldJarName
          : oldJarName.substring(0, versionStartIndex);

      // 在lib目录中查找具有相同artifactId的jar文件
      final Directory libDir = Directory(bootInfLibPath);
      if (await libDir.exists()) {
        final List<FileSystemEntity> files = await libDir.list().toList();
        File? matchingFile;

        for (var file in files) {
          if (file is File && p.extension(file.path) == '.jar') {
            final String fileName = p.basename(file.path);
            final int versionStartIndex = fileName.lastIndexOf('-');
            final String artifactId = versionStartIndex == -1
                ? fileName
                : fileName.substring(0, versionStartIndex);

            // 检查artifactId是否完全相同
            if (artifactId == oldArtifactId) {
              matchingFile = file;
              break;
            }
          }
        }

        if (matchingFile != null) {
          oldJarInTempDir = matchingFile;
        } else {
          throw Exception(
              "需要替换的Old Jar File 在BOOT-INF/lib中不存在，artifactId: $oldArtifactId");
        }
      } else {
        throw Exception("BOOT-INF/lib 目录不存在");
      }
    } else {
      // 原有的精确匹配逻辑
      oldJarInTempDir =
          File('$bootInfLibPath${p.separator}${oldJarFile!.name}');
      if (!(await oldJarInTempDir.exists())) {
        throw Exception(
            "需要替换的Old Jar File 在BOOT-INF/lib中不存在，${oldJarInTempDir.path}");
      }
    }

    // 删除找到的旧jar文件
    await oldJarInTempDir.delete();

    final String originalJarFileName = p.basename(newJarFile!.path);
    final File newJarCopied = await File(newJarPath)
        .copy('$bootInfLibPath${p.separator}$originalJarFileName');
    updateProgress(20 / size);
  }

  bool checkParam() {
    // 替换目录，目录目录必填
    if (selectType == 1) {
      if (_targetPathController.text.isEmpty) {
        showInfoBar(
            message: '请选择需要替换的Springboot Jar包',
            infoBarSeverity: InfoBarSeverity.error);
        return false;
      }
      if (selectReplaceDirType == 0 && _oriPathController.text.isEmpty) {
        showInfoBar(
            message: '请输入源目录路径', infoBarSeverity: InfoBarSeverity.error);
        return false;
      }
      if (selectReplaceDirType == 1) {
        if (null == selectReplaceOriZipFile) {
          showInfoBar(
              message: '请选择zip文件', infoBarSeverity: InfoBarSeverity.error);
          return false;
        } else if (!File(selectReplaceOriZipFile!.path).existsSync()) {
          showInfoBar(
              message: 'zip文件${selectReplaceOriZipFile!.path}不存在!',
              infoBarSeverity: InfoBarSeverity.error);
          return false;
        }
      }
      // 检查目标路径 BOOT-INF\classes\agcim\viewer\ui  或 BOOT-INF/classes/agcim/viewer/ui
      var targetPath = _targetPathController.text;
      if (targetPath.startsWith("/") || targetPath.startsWith("\\")) {
        showInfoBar(message: "SpringbootJar目标路径，应该为相对路径!");
        return false;
      }
    } else if (selectType == 0) {
      if (oldJarFile == null || newJarFile == null || workspaceDir == null) {
        showInfoBar(
            message: '请填写所有必填项', infoBarSeverity: InfoBarSeverity.error);
        return false;
      }
    }
    return true;
  }

  Future<void> processEachFile() async {
    setState(() {
      progress = 0.0;
    });
    // 检查参数
    var result = checkParam();
    if (!result) {
      return;
    }

    // 通用检查
    if (springBootJarFiles.isEmpty) {
      showInfoBar(
          message: '请选择需要替换的Springboot Jar包',
          infoBarSeverity: InfoBarSeverity.error);
      return;
    }
    for (var file in springBootJarFiles) {
      try {
        await replaceAndRepackSingle(springBootJarFiles.length, file);
      } catch (e) {
        // ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('处理文件时出错: ${p.basename(file.path)}, 错误信息: $e')));
        showInfoBar(
            message: '处理文件时出错: ${p.basename(file.path)}, 错误信息: $e',
            infoBarSeverity: InfoBarSeverity.error);
      }
    }
    setState(() {
      console = '';
      progress = 0.0;
    });
  }

  Future<void> openOutputDirectory() async {
    if (outputDir == null) {
      // ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('请先选择工作空间')));
      showInfoBar(message: '请先选择输出目录');
      return;
    }

    if (outputDir != null && !outputDir!.existsSync()) {
      // ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('输出目录不存在')));
      showInfoBar(message: '输出目录不存在');
      return;
    }
    final String outputDirectory = outputDir!.path;
    try {
      if (Platform.isWindows) {
        await Process.run('explorer', [outputDirectory]);
      } else if (Platform.isMacOS) {
        await Process.run('open', [outputDirectory]);
      } else if (Platform.isLinux) {
        await Process.run('xdg-open', [outputDirectory]);
      }
    } catch (e) {
      // ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('无法打开输出目录: $e')));
      showInfoBar(
          message: '无法打开输出目录: $e', infoBarSeverity: InfoBarSeverity.error);
    }
  }

  // 显示保存配置对话框
  Future<void> showSaveConfigDialog() async {
    String configName = currentConfigName ?? '配置${configNames.length + 1}';
    final TextEditingController _configNameController =
        TextEditingController(text: configName);

    final result = await showDialog<bool>(
      context: context,
      builder: (context) => ContentDialog(
        title: Text('保存配置'),
        content: Container(
            width: 300,
            height: 50,
            child: TextBox(
              controller: _configNameController,
              placeholder: '配置名称',
              autofocus: true,
            )),
        actions: [
          Button(
            onPressed: () => Navigator.pop(context, false),
            child: Text('取消'),
          ),
          Button(
            onPressed: () => Navigator.pop(context, true),
            child: Text('确定'),
          ),
        ],
      ),
    );

    if (result == true && _configNameController.text.trim().isNotEmpty) {
      await saveConfig(_configNameController.text.trim());
    }
  }

  Future<void> saveConfig(String configName) async {
    final controller = Get.find<ConfigController>();

    // 保存配置内容
    final config = {
      'springBootJars': springBootJarFiles.map((file) => file.path).toList(),
      'oldJar': oldJarFile?.path,
      'newJar': newJarFile?.path,
      'workspaceDir': workspaceDir?.path,
      'selectType': selectType, // Save the select type
      'targetPath': _targetPathController.text,
      'oriPath': _oriPathController.text,
      'oriZipSubPath': _oriZipSubPathController.text,
      'selectReplaceDirType': selectReplaceDirType,
      'replaceOriZipFile': selectReplaceOriZipFile?.path,
      'ignoreJarVersion': ignoreJarVersion, // 添加忽略版本配置
    };

    // 使用ConfigController保存配置
    final configExists =
        controller.configurations.any((c) => c.name == configName);
    if (!configExists) {
      // 创建新配置
      controller.createConfiguration(configName, config);
    } else {
      // 更新现有配置
      final configObj = controller.getConfigByName(configName);
      if (configObj != null) {
        controller.updateConfiguration(configObj.id, config);
      }
    }

    // 切换到该配置
    controller.switchConfiguration(configName);

    // 更新本地状态
    configNames = controller.configurations.map((c) => c.name).toList();
    currentConfigName = configName;

    showInfoBar(
        message: '配置已保存：$configName', infoBarSeverity: InfoBarSeverity.success);
  }

  // 加载配置名称列表
  Future<void> loadConfigNames() async {
    final controller = Get.find<ConfigController>();
    // 控制器已在main.dart中初始化
    // 从Controller获取配置列表
    configNames = controller.configurations.map((c) => c.name).toList();
    currentConfigName = controller.currentConfig?.value?.name ?? '';
  }

  // 根据名称加载配置
  Future<void> loadConfigByName(String configName) async {
    final controller = Get.find<ConfigController>();

    // 控制器已在main.dart中初始化

    // 从Controller获取配置数据
    final config = await controller.getConfigData(configName);

    if (config != null) {
      // 切换到该配置
      controller.switchConfiguration(configName);

      setState(() {
        // 更安全地处理springBootJars
        final List<dynamic>? springBootJarsData = config['springBootJars'];
        if (springBootJarsData != null) {
          springBootJarFiles = springBootJarsData
              .where((path) => path != null)
              .map((path) => File(path.toString()))
              .toList();
        } else {
          springBootJarFiles = [];
        }
        oldJarFile = config['oldJar'] != null ? XFile(config['oldJar']) : null;
        newJarFile = config['newJar'] != null ? XFile(config['newJar']) : null;

        _oldJarController.text = config['oldJar'] ?? '';
        _newJarController.text = config['newJar'] ?? '';

        selectType = config['selectType'] ?? 0;
        _targetPathController.text = config['targetPath'] ?? '';
        _oriPathController.text = config['oriPath'] ?? '';
        _oriZipSubPathController.text = config['oriZipSubPath'] ?? '';
        selectReplaceDirType = config['selectReplaceDirType'] ?? 0;
        selectReplaceOriZipFile = config['replaceOriZipFile'] != null
            ? XFile(config['replaceOriZipFile'])
            : null;
        _selectReplaceOriZipFileController.text =
            config['replaceOriZipFile'] ?? "";

        // 更新工作空间和输出目录
        if (config['workspaceDir'] != null) {
          workspaceDir = Directory(config['workspaceDir']);
          _workspacePathController.text = config['workspaceDir'];
        }

        // 加载忽略版本配置
        ignoreJarVersion = config['ignoreJarVersion'] ?? false;

        currentConfigName = configName;
      });

      showInfoBar(
          message: '已加载配置：$configName',
          infoBarSeverity: InfoBarSeverity.success);
    }
  }

  // 删除配置
  Future<void> deleteConfig(String configName) async {
    final controller = Get.find<ConfigController>();

    // 获取配置对象以获取正确的ID
    final configObj = controller.getConfigByName(configName);
    if (configObj != null) {
      // 使用Controller删除配置
      controller.deleteConfiguration(configObj.id);

      // 更新本地状态
      configNames = controller.configurations.map((c) => c.name).toList();
      currentConfigName = controller.currentConfig?.value?.name ?? '';

      showInfoBar(
          message: '配置已删除：$configName',
          infoBarSeverity: InfoBarSeverity.success);
    } else {
      showInfoBar(
          message: '未找到配置：$configName', infoBarSeverity: InfoBarSeverity.error);
    }
  }

  // 显示配置选择对话框
  Future<void> showConfigSelectionDialog() async {
    final controller = Get.find<ConfigController>();
    // 控制器已在main.dart中初始化
    // 从Controller获取配置列表
    configNames = controller.configurations.map((c) => c.name).toList();

    final result = await showDialog<String>(
      context: context,
      builder: (context) => ContentDialog(
        title: Text('选择配置'),
        content: Container(
          width: 300,
          height: 250,
          child: configNames.isEmpty
              ? Center(child: Text('没有保存的配置'))
              : ListView.builder(
                  itemCount: configNames.length,
                  itemBuilder: (context, index) {
                    final name = configNames[index];
                    return Card(
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          Expanded(
                            child: Button(
                              onPressed: () {
                                // 关闭对话框并返回选择的配置名称
                                Navigator.pop(context, name);
                              },
                              style: ButtonStyle(
                                padding:
                                    WidgetStateProperty.all(EdgeInsets.all(12)),
                                backgroundColor:
                                    WidgetStateProperty.all(Colors.transparent),
                              ),
                              child: Text(name),
                            ),
                          ),
                          Button(
                            child: Icon(icon.FluentIcons.delete_16_regular),
                            onPressed: () async {
                              // 先关闭配置选择对话框
                              Navigator.pop(context);

                              // 然后显示确认删除对话框
                              final confirm = await showDialog<bool>(
                                context: context,
                                builder: (context) => ContentDialog(
                                  title: Text('确认删除'),
                                  content: Text('确定要删除配置 "$name" 吗？'),
                                  actions: [
                                    Button(
                                      onPressed: () =>
                                          Navigator.pop(context, false),
                                      child: Text('取消'),
                                    ),
                                    Button(
                                      onPressed: () =>
                                          Navigator.pop(context, true),
                                      child: Text('删除'),
                                      style: ButtonStyle(
                                        backgroundColor:
                                            WidgetStateProperty.all(
                                                Colors.red.withOpacity(0.1)),
                                        foregroundColor:
                                            WidgetStateProperty.all(Colors.red),
                                      ),
                                    ),
                                  ],
                                ),
                              );
                              if (confirm == true) {
                                await deleteConfig(name);
                                // 重新加载配置列表
                                configNames = controller.configurations
                                    .map((c) => c.name)
                                    .toList();
                              }
                            },
                          ),
                        ],
                      ),
                    );
                  },
                ),
        ),
        actions: [
          Button(
            onPressed: () => Navigator.pop(context),
            child: Text('关闭'),
          ),
        ],
      ),
    );

    if (result != null) {
      await loadConfigByName(result);
    }
  }

  Future<void> loadConfig() async {
    final prefs = await SharedPreferences.getInstance();

    // 加载配置名称列表
    await loadConfigNames();

    // 如果有当前配置名称且不为空，加载该配置
    if (currentConfigName != null && currentConfigName!.isNotEmpty) {
      await loadConfigByName(currentConfigName!);
      return;
    }

    // 向后兼容：如果有旧格式的配置，加载它并保存为新格式
    final oldConfigString = prefs.getString('config');
    if (oldConfigString != null) {
      final oldConfig = jsonDecode(oldConfigString);
      setState(() {
        // 更安全地处理springBootJars
        final List<dynamic>? springBootJarsData = oldConfig['springBootJars'];
        if (springBootJarsData != null) {
          springBootJarFiles = springBootJarsData
              .where((path) => path != null)
              .map((path) => File(path.toString()))
              .toList();
        } else {
          springBootJarFiles = [];
        }
        oldJarFile =
            oldConfig['oldJar'] != null ? XFile(oldConfig['oldJar']) : null;
        newJarFile =
            oldConfig['newJar'] != null ? XFile(oldConfig['newJar']) : null;

        _oldJarController.text = oldConfig['oldJar'] ?? '';
        _newJarController.text = oldConfig['newJar'] ?? '';

        selectType = oldConfig['selectType'] ?? 0;
        _targetPathController.text = oldConfig['targetPath'] ?? '';
        _oriPathController.text = oldConfig['oriPath'] ?? '';
        _oriZipSubPathController.text = oldConfig['oriZipSubPath'] ?? '';
        selectReplaceDirType = oldConfig['selectReplaceDirType'] ?? 0;
        selectReplaceOriZipFile = oldConfig['replaceOriZipFile'] != null
            ? XFile(oldConfig['replaceOriZipFile'])
            : null;
        _selectReplaceOriZipFileController.text =
            oldConfig['replaceOriZipFile'] ?? "";
      });

      // 自动保存为新格式
      await saveConfig('默认配置');

      // 删除旧格式配置
      prefs.remove('config');
      return;
    }

    // 如果既没有当前配置也没有旧格式配置，创建并加载默认配置
    setState(() {
      springBootJarFiles = [];
      oldJarFile = null;
      newJarFile = null;
      _oldJarController.text = '';
      _newJarController.text = '';
      selectType = 0;
      _targetPathController.text = '';
      _oriPathController.text = '';
      _oriZipSubPathController.text = '';
      selectReplaceDirType = 0;
      selectReplaceOriZipFile = null;
      _selectReplaceOriZipFileController.text = '';
    });

    // 保存默认配置
    await saveConfig('默认配置');
    // 加载新创建的默认配置
    await loadConfigByName('默认配置');

    // 显示提示 - 使用Fluent UI的通知方式
    showDialog(
      context: context,
      builder: (context) {
        return ContentDialog(
          title: Text('提示'),
          content: Text('已创建默认配置'),
          actions: [
            FilledButton(
              child: Text('确定'),
              onPressed: () => Navigator.pop(context),
            ),
          ],
        );
      },
    );
    // 延迟关闭对话框以确保用户看到提示
    Future.delayed(Duration(seconds: 2), () {
      if (mounted) {
        Navigator.pop(context);
      }
    });
  }

  Future<void> selectSpringBootJars() async {
    FilePickerResult? result = await FilePicker.platform.pickFiles(
      type: FileType.custom,
      allowedExtensions: ['jar'],
      allowMultiple: true, // 支持多选
    );

    if (result != null) {
      setState(() {
        springBootJarFiles = result.paths
            .where((path) => path != null)
            .map((path) => File(path!))
            .toList();
      });
    }
  }

  /// 获取路径的驱动器字母（适用于Windows）
  String getDriveLetter(String path) {
    return p
        .split(path)[0]
        .toUpperCase(); // Convert to uppercase for comparison.
  }

  /// Recursively copies a directory to a new location.
  Future<void> copyDirectory(Directory source, Directory destination) async {
    await destination.create(recursive: true);

    var entities = source.listSync(recursive: false, followLinks: false);

    for (var entity in entities) {
      final targetPath = p.join(destination.path, p.basename(entity.path));

      if (entity is File) {
        await File(entity.path).copy(targetPath);
      } else if (entity is Directory) {
        await copyDirectory(entity, Directory(targetPath));
      }
    }
  }

  /// Moves a directory from one location to another with handling for existing destination directories.
  Future<void> moveDirectory(String oldPath, String newPath,
      {bool overwrite = false, bool merge = false}) async {
    try {
      final oldDir = Directory(oldPath);
      final newDir = Directory(newPath);

      // Check if the destination directory already exists.
      bool destinationExists = await newDir.exists();

      if (destinationExists) {
        if (!overwrite && !merge) {
          throw Exception('目标目录已存在，未选择覆盖或合并');
        }

        if (overwrite) {
          print('覆盖模式：正在删除现有目录...');
          await newDir.delete(
              recursive:
                  true); // Delete the existing directory if overwrite is true.
        }

        if (merge) {
          print('合并模式：正在保留现有文件并添加新文件...');
          // In merge mode, we don't need to do anything special here because we just copy over the files.
        }
      }

      // Check if source and destination are on the same drive by comparing the first two characters.
      bool isSameDrive = getDriveLetter(oldPath) == getDriveLetter(newPath);

      if (isSameDrive) {
        // If on the same drive, use rename.
        await oldDir.rename(newPath);
        print('目录在同一分区上移动成功');
      } else {
        // If on different drives, copy and then delete the original.
        await copyDirectory(oldDir, newDir);

        // Ensure the new directory exists and contains files before deleting the old one.
        if (await newDir.exists()) {
          // Check if the new directory contains any files or subdirectories.
          List<FileSystemEntity> contents = await newDir.list().toList();
          if (contents.isNotEmpty) {
            await oldDir.delete(recursive: true);
            print('目录跨分区移动成功');
          } else {
            throw Exception('复制目录失败，新目录为空');
          }
        } else {
          throw Exception('复制目录失败，新目录不存在');
        }
      }
    } catch (e) {
      print('目录移动失败: $e');
    }
  }

  Future<void> unzipWith7z(String zipFilePath, String outputPath) async {
    try {
      // Ensure the output directory exists.
      await Directory(outputPath).create(recursive: true);

      // 读取7z的绝对路径
      // returns the abolute path of the executable file of your app:
      String real7zExePath = get7zExePath();

      // Define the command and arguments for 7z.
      List<String> command = [
        '${real7zExePath + p.separator}7z.exe', // Command to run
        'x', // Extract files with full paths
        '-o$outputPath', // Output directory
        zipFilePath, // Path to the zip file
        '-bsp1',
        '-bb3'
      ];

      // Run the process.
      // final result = await Process.run(command[0], command.skip(1).toList());
      final process = await Process.start(command[0], command.skip(1).toList());
      // 使用正确的编码器，例如GBK编码
      // 使用正则表达式匹配路径和进度信息
      RegExp pathPattern = RegExp(r'^- (.*)$');
      RegExp progressPattern = RegExp(r'^(\d+%) (\d+)$');
      // 订阅标准输出流
      process.stdout.transform(gbk.decoder).listen((data) {
        // print("${data}");
        // 解析输出数据以提取进度信息
        // 注意：7z.exe 的输出格式可能会有所不同，因此你需要根据实际情况调整正则表达式或其他解析逻辑
        Iterable<String> lines = LineSplitter().convert(data);
        List<String> result = [];
        String? currentPath;
        for (var line in lines) {
          setState(() {
            console = '正在解压: ' + p.basename(zipFilePath) + ', ' + line;
          });
        }

        // 使用正则表达式解析进度信息
/*        RegExp regExp = RegExp(r"(\d+)%\s+\d+");
        Iterable<Match> matches = regExp.allMatches(data);
        for (Match m in matches) {
          int percent = int.tryParse(m.group(1) ?? "0") ?? 0;
          if (percent >= 0 && percent <= 100) {
            // onProgress(percent / 100); // 将百分比转换为0-1之间的值
          }
        }*/
      });

      // Print the standard output and error from the process.
      // print('Standard output: ${result.stdout}');
      // print('Standard error: ${result.stderr}');

      if (await process.exitCode == 0) {
        print('解压成功！');
      } else {
        print('解压失败: 7z命令执行失败');
      }
    } catch (e) {
      print('解压失败: $e');
    }
  }

  String get7zExePath() {
    String mainPath = Platform.resolvedExecutable;
    // remove from that path the name of the executable file of your app:
    mainPath = mainPath.substring(0, mainPath.lastIndexOf("\\"));
    // concat the path with '\data\flutter_assets\assets\exe', where 'exe' is the
    // directory where are the executable files you want to run from your app:
    String real7zExePath =
        "$mainPath${p.separator}data${p.separator}flutter_assets${p.separator}assets${p.separator}exe";
    return real7zExePath;
  }

  /**
   * 解压zip文件文件并替换旧目录（删除旧目录）
   */
  Future<void> unzipAndReplaceTargetPath(Directory tempDir, int size) async {
    if (selectType == 1 &&
        selectReplaceDirType == 1 &&
        selectReplaceOriZipFile == null) {
      showInfoBar(
          message: '请选择源Zip文件和目标路径', infoBarSeverity: InfoBarSeverity.error);
      return;
    }
    //  若选择了使用zip文件作为输入，则复制zip文件至工作空间,在工作空间中解压zip文件, 根据选择的zip文件以及子目录，得出替换的源目录
    // 源目录
    String oriPath;
    // 删除旧的目标目录
    String? tempZipFilePath;
    String? unzipTargetDirectory;
    if (selectType == 1 && selectReplaceDirType == 1) {
      final String zipFilePath = selectReplaceOriZipFile!.path;
      tempZipFilePath = p.join(tempDir.path, p.basename(zipFilePath));
      // 解压zip的目标目录
      unzipTargetDirectory =
          p.join(tempDir.path, p.basenameWithoutExtension(tempZipFilePath));
      // Copy the selected zip file to the temp directory.
      await File(zipFilePath).copy(tempZipFilePath);
      // 复制Springboot Jar到工作空间 10%
      updateProgress(15 / size);
      // Read and decode the copied zip file in the temp directory.
      // await unzipFileToDirectory(tempZipFilePath, unzipTargetDirectory);
      // await extractFileToDisk(tempZipFilePath, unzipTargetDirectory);
      await unzipWith7z(tempZipFilePath, unzipTargetDirectory);
      updateProgress(20 / size);
      final String subPath = _oriZipSubPathController.text.trim();
      oriPath = subPath.isEmpty
          ? unzipTargetDirectory
          : p.join(unzipTargetDirectory, subPath);
    } else {
      oriPath = _oriPathController.text;
    }

    try {
      // 目标目录（临时目录 + 目录路径(相对路径))
      String targetPath = _targetPathController.text.trim();
      // 路径自动修正
      if (Platform.isWindows) {
        targetPath = targetPath.replaceAll("/", p.separator);
      } else {
        targetPath = targetPath.replaceAll("\\", p.separator);
      }

      Directory targetDir = Directory(p.join(tempDir.path, targetPath));
      // Ensure the target directory exists and is empty.
      if (await targetDir.exists()) await targetDir.delete(recursive: true);
      // 重新创建目录目录
      await targetDir.create();
      // zip子目录路径
      final String subPath = _oriZipSubPathController.text.trim();
      final String basePath = p.basename(
          targetPath); // Assuming targetPath ends with a directory name.
      // 移动文件
      setState(() {
        console = '替换目标目录： ${oriPath} ==> ${targetDir.path}';
      });
      await moveDirectory(oriPath, targetDir.path, overwrite: true);
      // 替换目录 5%
      updateProgress(5 / size);
      showInfoBar(
          message: '解压并替换完成！', infoBarSeverity: InfoBarSeverity.success);
    } catch (e) {
      showInfoBar(
          message: '解压并替换失败: $e', infoBarSeverity: InfoBarSeverity.error);
    } finally {
      // Optionally clean up the copied zip file in the temp directory.
      if (null != tempZipFilePath) {
        final File tempZipFile = File(tempZipFilePath);
        if (await tempZipFile.exists()) {
          await tempZipFile.delete();
        }
      }
      // 删除解压zip文件目录
      if (null != unzipTargetDirectory) {
        final Directory unzipTargetDirectoryFile =
            Directory(unzipTargetDirectory);
        if (unzipTargetDirectoryFile.existsSync()) {
          unzipTargetDirectoryFile.deleteSync();
        }
      }
    }

    updateProgress(20 / size);
    showInfoBar(message: '解压并替换完成！', infoBarSeverity: InfoBarSeverity.success);
  }

  @override
  void initState() {
    super.initState();
    loadConfig(); // Load saved configuration on startup
    _controller = AnimationController(
      duration: const Duration(seconds: 1),
      vsync: this, // 使用 SingleTickerProviderStateMixin 提供的 vsync
    );
  }

  @override
  void dispose() {
    _controller.dispose(); // 确保在小部件销毁时释放资源
    super.dispose();
  }

  /**
   * 开始或结束旋转
   */
  void _startOrStopRotation() {
    setState(() {
      if (_isRotating) {
        _controller.stop(canceled: true);
      } else {
        _controller.repeat();
      }
      _isRotating = !_isRotating;
    });
  }

  @override
  Widget build(BuildContext context) {
    // 使用GetX获取配置控制器
    final configController = Get.find<ConfigController>();
    // 初始化控制器的值
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _workspacePathController.text =
          configController.currentConfig?.value?.workspaceDir ?? '';
      // 输出目录
      outputDir =
          Directory(configController.currentConfig?.value?.outputDir ?? '');
      // 工作空间目录
      workspaceDir =
          Directory(configController.currentConfig?.value?.workspaceDir ?? '');
    });

    return GetX<ThemeController>(builder: (themeController) {
      return Acrylic(
        // 设置应用于 Acrylic 效果的颜色。这个颜色会与底层内容混合。
        tint: Colors.white.withOpacity(0.1),
        //tintAlpha: 0.5,
        // 设置模糊的程度，数值越大，模糊效果越强。
        blurAmount: 0.0, // 设置模糊程度
        // 阴影效果
        elevation: 0,
        child: Container(
          color: themeController.isDark
              ? Colors.black.withOpacity(0.8)
              : Colors.white.withOpacity(0.5),
          child: Padding(
              padding: const EdgeInsets.all(16.0),
              child: Column(
                mainAxisSize: MainAxisSize.max, // 只占用所需的空间
                crossAxisAlignment: CrossAxisAlignment.start,
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: [
                  Row(
                    children: [
                      Expanded(
                          child: ToggleButton(
                        child: const SizedBox(
                          width: 160,
                          child: Text('替换lib目录下的jar'),
                        ),
                        checked: selectType == 0,
                        onChanged: (bool value) {
                          if (value) {
                            setState(() {
                              selectType = 0;
                            });
                          } else {
                            setState(() {
                              selectType = 1;
                            });
                          }
                        },
                      )),
                      Expanded(
                          child: ToggleButton(
                        child: const SizedBox(
                          width: 160,
                          child: Text('替换目录'),
                        ),
                        checked: selectType == 1,
                        onChanged: (bool value) {
                          if (value) {
                            setState(() {
                              selectType = 1;
                            });
                          } else {
                            setState(() {
                              selectType = 0;
                            });
                          }
                        },
                      )),
                    ],
                  ),
                  Row(
                    children: [
                      SizedBox(
                        width: textWidth,
                        child: Text("BootJar文件:", style: TextStyle(color: themeController.isDark?Colors.white: Colors.black)),
                      ),
                      Expanded(
                          child: Container(
                        decoration: BoxDecoration(
                          color: themeController.isDark
                              ? Colors.white.withOpacity(0.06)
                              : Colors.white,
                          // borderRadius: BorderRadius.circular(10), // 圆角
                          border: Border.all(
                            color: Colors.grey.withOpacity(0.3),
                            width: 1.0,
                          ),
                          boxShadow: [
                            BoxShadow(
                              color: Colors.grey.withOpacity(0.05), // 阴影颜色
                              spreadRadius: 5, // 扩展半径
                              blurRadius: 2, // 模糊半径
                              offset: Offset(0, 3), // 阴影相对于组件的偏移量
                            ),
                          ],
                        ),
                        child: IconButton(
                          icon: Icon(
                            icon.FluentIcons.add_24_regular,
                            size: 24.0,
                          ),
                          onPressed: selectSpringBootJars,
                        ),
                      ))
                    ],
                  ),
                  Visibility(
                    visible: springBootJarFiles.isNotEmpty,
                    maintainState: true,
                    maintainAnimation: true,
                    maintainSize: true,
                    child: Container(
                      height: 280, // 设置固定高度或根据需要调整
                      child: ListView.builder(
                        itemCount: springBootJarFiles.length,
                        scrollDirection: Axis.vertical,
                        itemBuilder: (context, index) {
                          final file = springBootJarFiles[index];
                          final fileName = p.basename(file.path); // 只显示文件名

                          return ListTile(
                            leading: Image.asset('assets/images/jar.png',
                                width: 24, height: 24),
                            title:
                                Text(fileName, overflow: TextOverflow.ellipsis),
                            subtitle: Text(file.path,
                                overflow: TextOverflow.ellipsis),
                            trailing: IconButton(
                              icon: const Icon(
                                  icon.FluentIcons.dismiss_12_filled,
                                  size: 18.0),
                              onPressed: () {
                                setState(() {
                                  springBootJarFiles.removeAt(index);
                                });
                              },
                            ),
                          );
                        },
                      ),
                      padding: EdgeInsets.only(left: textWidth - 15),
                    ),
                  ),
                  Visibility(
                      visible: springBootJarFiles.isNotEmpty,
                      maintainState: true,
                      maintainAnimation: true,
                      maintainSize: true,
                      child: Text('已选择: ${springBootJarFiles.length}个文件', style: TextStyle(color: themeController.isDark?Colors.white: Colors.black))),
                  if (selectType == 0)
                    Row(
                      children: [
                        SizedBox(
                          width: textWidth,
                          child: Text('替换目标Jar文件:', style: TextStyle(color: themeController.isDark?Colors.white: Colors.black)),
                        ),
                        Expanded(
                          child: TextBox(
                            controller: _oldJarController,
                            readOnly: true,
                          ),
                        ),
                        if (oldJarFile != null)
                          IconButton(
                            icon: const Icon(icon.FluentIcons.dismiss_12_filled,
                                size: 18.0),
                            onPressed: () {
                              setState(() {
                                oldJarFile = null;
                                _oldJarController.text = '';
                              });
                            },
                          ),
                        IconButton(
                          icon: Icon(
                            icon.FluentIcons.arrow_upload_32_regular,
                            size: 24.0,
                          ),
                          onPressed: () => selectFile('oldJar'),
                        )
                      ],
                    )
                  else if (selectType == 1)
                    Row(
                      children: [
                        SizedBox(
                            width: textWidth,
                            child: Row(
                              children: [
                                Text("替换目标目录：", style: TextStyle(color: themeController.isDark?Colors.white: Colors.black)),
                                Tooltip(
                                  message:
                                      '替换BootJar的相对路径。选择BootJar对应的路径后将自动提取相对路径。',
                                  child: Image.asset('assets/images/help.png',
                                      width: 18, height: 18),
                                ),
                              ],
                            )),
                        Expanded(
                          child: TextBox(
                            placeholder: '输入替换的目标路径',
                            controller: _targetPathController,
                          ),
                        ),
                        IconButton(
                          icon: const Icon(icon.FluentIcons.folder_48_regular,
                              size: 24.0),
                          onPressed: () {
                            selectReplaceTargetDir();
                          },
                        ),
                      ],
                    ),
                  if (selectType == 0)
                    Row(
                      children: [
                        SizedBox(
                          width: textWidth,
                          child: Text('新的Jar文件:', style: TextStyle(color: themeController.isDark?Colors.white: Colors.black)),
                        ),
                        Expanded(
                          child: TextBox(
                            controller: _newJarController,
                            readOnly: true,
                          ),
                        ),
                        if (newJarFile != null)
                          IconButton(
                            icon: const Icon(icon.FluentIcons.dismiss_12_filled,
                                size: 18.0),
                            onPressed: () {
                              setState(() {
                                newJarFile = null;
                                _newJarController.text = '';
                              });
                            },
                          ),
                        IconButton(
                          icon: Icon(
                            icon.FluentIcons.arrow_upload_24_regular,
                            size: 24.0,
                          ),
                          onPressed: () => selectFile('newJar'),
                        )
                      ],
                    )
                  else
                    Row(
                      children: [
                        SizedBox(
                          width: textWidth,
                          child: Text("替换类型:"),
                        ),
                        ComboBox<int>(
                            value: selectReplaceDirType,
                            items: typesMap.entries.map((e) {
                              return ComboBoxItem(
                                child: Text(e.value),
                                value: e.key,
                              );
                            }).toList(),
                            onChanged: (type) => setState(() {
                                  if (type != null) {
                                    selectReplaceDirType = type;
                                  }
                                })),
                      ],
                    ),
                  if (selectType == 1 && selectReplaceDirType == 0)
                    Row(
                      children: [
                        SizedBox(
                          width: textWidth,
                          child: Text('源路径:'),
                        ),
                        Expanded(
                          child: TextBox(
                            controller: _oriPathController,
                            readOnly: true,
                          ),
                        ),
                        SizedBox(width: 8),
                        Row(
                          children: [
                            IconButton(
                              icon: Icon(
                                icon.FluentIcons.folder_48_regular,
                                size: 24.0,
                              ),
                              onPressed: selectReplaceOriDir,
                            ),
                          ],
                        )
                      ],
                    )
                  else if (selectType == 1 && selectReplaceDirType == 1)
                    Row(
                      children: [
                        SizedBox(
                          width: textWidth,
                          child: Text('Zip文件:'),
                        ),
                        Expanded(
                          child: TextBox(
                            controller: _selectReplaceOriZipFileController,
                            readOnly: true,
                          ),
                        ),
                        if (selectReplaceOriZipFile != null)
                          IconButton(
                            icon: const Icon(icon.FluentIcons.dismiss_12_filled,
                                size: 18.0),
                            onPressed: () {
                              setState(() {
                                selectReplaceOriZipFile = null;
                                _selectReplaceOriZipFileController.text = '';
                              });
                            },
                          ),
                        IconButton(
                          icon: Icon(
                            icon.FluentIcons.folder_zip_24_regular,
                            size: 24.0,
                          ),
                          onPressed: () => selectFile('replaceOriZip'),
                        ),
                      ],
                    ),
                  if (selectType == 1 && selectReplaceDirType == 1)
                    Row(children: [
                      SizedBox(
                          width: textWidth,
                          child: Row(
                            children: [
                              Text("Zip子目录："),
                              Tooltip(
                                message:
                                    '输入需要替换的Zip子目录路径(不输入子目录路径将自动将Zip文件解压后的最外层的目录替换至目标目录)',
                                child: Image.asset('assets/images/help.png',
                                    width: 18, height: 18),
                              ),
                            ],
                          )),
                      Expanded(
                        child: TextBox(
                          placeholder: '输入子目录路径',
                          controller: _oriZipSubPathController,
                        ),
                      ),
                      Tooltip(
                          message: "自动读取zip文件子目录",
                          child: AnimatedBuilder(
                            animation: _controller,
                            builder: (context, child) {
                              return Transform.rotate(
                                angle: _controller.value * 2 * 3.14,
                                // 旋转角度从 0 到 2π
                                child: IconButton(
                                  icon: Icon(
                                    icon.FluentIcons.arrow_sync_20_regular,
                                    size: 24.0,
                                  ),
                                  onPressed: selectReplaceOriZipFile == null
                                      ? null
                                      : readSubDir,
                                ),
                              );
                            },
                          ))
                    ]),
                  Visibility(
                      visible: _showInfoBar,
                      maintainState: true,
                      maintainAnimation: true,
                      maintainSize: true,
                      child: InfoBar(
                        severity: _infoBarSeverity,
                        title: Text(''),
                        content: Text(
                          _infoBarMessage,
                          overflow: TextOverflow.ellipsis,
                          maxLines: 1,
                        ),
                      )),
                  Text(
                    console, maxLines: 1, // 限制最大显示2行
                    overflow: TextOverflow.ellipsis, // 超出部分显示省略号
                    style: TextStyle(fontSize: 16),
                  ),
                  Visibility(
                    visible: true,
                    maintainState: true,
                    maintainAnimation: true,
                    maintainSize: true,
                    child: Row(children: [
                      Expanded(
                        child: ProgressBar(value: progress),
                      ),
                      // Progress bar
                      SizedBox(width: 8), // 添加一些间距
                      Text(
                        '${(progress).toStringAsFixed(0)}%',
                        style: TextStyle(fontSize: 24, color: themeController.isDark?Colors.white: Colors.black),
                      ),
                    ]),
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    // 水平间距最大化
                    children: [
                      Row(
                        children: [
                          Button(
                            onPressed: showSaveConfigDialog,
                            child: Text('保存配置'),
                          ),
                          SizedBox(width: 8),
                          Button(
                            onPressed: showConfigSelectionDialog,
                            child: Text('加载配置'),
                          ),
                          SizedBox(width: 8),
                          // 添加忽略版本的复选框
                          Checkbox(
                            checked: ignoreJarVersion,
                            onChanged: (bool? value) {
                              setState(() {
                                ignoreJarVersion = value ?? false;
                              });
                            },
                          ),
                          SizedBox(width: 4),
                          Text('忽略Jar版本进行替换', style: TextStyle(color: themeController.isDark?Colors.white: Colors.black)),
                        ],
                      ),
                      Center(
                        child: FilledButton(
                            onPressed: progress != 0 ? null : processEachFile,
                            style: ButtonStyle(
                              backgroundColor: progress != 0
                                  ? WidgetStateProperty.all(
                                      Colors.blue.withOpacity(0.7))
                                  : WidgetStateProperty.all(
                                      themeController.color),
                              foregroundColor:
                                  WidgetStateProperty.all(Colors.white),
                              elevation: WidgetStateProperty.all(8),
                            ),
                            child: Container(
                              width: 140,
                              child: Row(
                                mainAxisSize: MainAxisSize.min,
                                children: [
                                  Icon(
                                      progress == 0
                                          ? icon.FluentIcons.play_28_filled
                                          : icon.FluentIcons.pause_28_filled,
                                      color: Colors.white),
                                  SizedBox(width: 8),
                                  Container(
                                    alignment: Alignment.center,
                                    child: Text(
                                      progress == 0 ? '替换并重新打包' : "处理中",
                                      textAlign: TextAlign.center,
                                    ),
                                  )
                                ],
                              ),
                            )),
                      ),
                      Button(
                        onPressed: openOutputDirectory,
                        child: Text('打开输出目录'),
                      ),
                    ],
                  ),
                  // 当前配置名称显示
                  Visibility(
                    visible: currentConfigName != null,
                    child: Padding(
                      padding: const EdgeInsets.only(top: 8.0),
                      child: Text(
                        '当前配置: ${currentConfigName}',
                        style: TextStyle(
                          color: Get.find<ThemeController>().isDark
                              ? Colors.grey[120]
                              : Colors.grey[150],
                          fontSize: 12,
                        ),
                      ),
                    ),
                  )
                ],
              )),
        ),
      );
    });
  }
}
